package impl_v1;

import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
import java.util.HashMap;
import java.util.Map;

import model.AuctionInformation;
import model.AuctionPrivate;
import model.AuctionPublic;
import model.Bid;
import model.Item;
import model.WinnerInformation;
import model.exception.InvalidAuctionIDException;
import model.exception.InvalidPriceException;
import model.exception.UnavailableAuctionException;
import model.exception.WrongKeyException;
import model.factory.Factory;
import model.server.AuctioningSystem;

public class AuctioningSystemImpl extends UnicastRemoteObject implements
		AuctioningSystem {

	private static final long serialVersionUID = 1413576917957878882L;

	private final Factory factory;

	private final Map<Integer, AuctionPrivate> listAuctions;
	private int lastID;

	public AuctioningSystemImpl(Factory factory) throws RemoteException {
		super();
		this.factory = factory;
		this.lastID = 0;

		this.listAuctions = new HashMap<Integer, AuctionPrivate>();
	}

	@Override
	public Map<Integer, AuctionPrivate> getListAuctions() throws RemoteException {
		return listAuctions;
	}

	@Override
	public synchronized AuctionInformation createAuction(Item item, int startingPrice,
			int reservePrice) throws RemoteException {

		int auctionID = this.getLastID();
		this.lastID++;
		int key = (int) (Math.random() * Integer.MAX_VALUE);

		AuctionPrivate auction = factory.newAuctionPrivate(reservePrice, key,
				startingPrice, item);

		this.listAuctions.put(auctionID, auction);

		AuctionInformation auctionInformation = factory.newAuctionInformation(
				auctionID, auction.getKey());

		System.out.println("The auction " + auction + " is created");
		return auctionInformation;
	}

	@Override
	public synchronized WinnerInformation closeAuction(int auctionID, int key) throws RemoteException, InvalidAuctionIDException,
			WrongKeyException {
		if (this.listAuctions.containsKey(auctionID)) {

			AuctionPrivate auction = this.listAuctions.get(auctionID);
			if (auction.getKey() == key) {

				this.listAuctions.get(auctionID).setClosed();
				if (auction.hasBid()) {
					String name = auction.getBid().getName();
					String email = auction.getBid().getEmail();
					int finishingPrice = auction.getBid().getPrice();
					return factory.newBuyerInformation(name, email, finishingPrice);
				} else {
					return null;
				}

			} else {
				throw new WrongKeyException();
			}

		} else {
			throw new InvalidAuctionIDException();
		}
	}

	@Override
	public synchronized Map<Integer, AuctionPublic> listAuctionsPublic() throws RemoteException {
		Map<Integer, AuctionPublic> list = new HashMap<Integer, AuctionPublic>();
		for (int key : listAuctions.keySet()) {
			AuctionPrivate auction = listAuctions.get(key);
			list.put(key, getFactory().newAuctionPublic(auction));
		}
		return list;
	}

	@Override
	public synchronized void bid(int auctionID, int price, String name, String email) throws RemoteException,
			InvalidAuctionIDException, UnavailableAuctionException, InvalidPriceException {

		if (!getListAuctions().containsKey(auctionID)) {
			throw new InvalidAuctionIDException();
		}

		AuctionPrivate auction = getListAuctions().get(auctionID);

		if (auction.isClosed()) {
			throw new UnavailableAuctionException();
		}

		if (auction.getStartingPrice() >= price || (auction.getBid() != null && auction.getBid().getPrice() >= price)) {
			throw new InvalidPriceException();
		}

		Bid newBid = getFactory().newBid(price, name, email);
		auction.setBid(newBid);
		System.out.println("New bid for auction " + auction);
	}

	public int getLastID() {
		return lastID;
	}

	public void setLastID(int lastID) {
		this.lastID = lastID;
	}

	public Factory getFactory() {
		return factory;
	}
}
